// Copyright (c) 2008 Michael Buckley

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#import "BTLSocketBufferTests.h"

@implementation BTLSocketBufferTests

- (void)testReadSucceeded
{
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertTrue([testBuffer readSucceeded], nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertFalse([testBuffer readSucceeded], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
}

- (void)testInts
{
	int8_t firstValue = 64;
	int8_t secondValue = -64;
	int16_t thirdValue = 256;
	int16_t fourthValue = -256;
	int32_t fifthValue = 800000;
	int32_t sixthValue = -800000;
	int64_t seventhValue = 5000000000LL;
	int64_t eighthValue = -5000000000LL;
	
	int testLength = 5;
	char* ninthValue = (char*) malloc(testLength);
	memcpy(ninthValue, "12345", testLength);
	
	
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	
	[testBuffer addInt8:firstValue];
	[testBuffer addInt8:secondValue];
	[testBuffer addInt16:thirdValue];
	[testBuffer addInt16:fourthValue];
	[testBuffer addInt32:fifthValue];
	[testBuffer addInt32:sixthValue];
	[testBuffer addInt64:seventhValue];
	[testBuffer addInt64:eighthValue];
	[testBuffer addInt:(void*)ninthValue ofSize:testLength];
	
	[testBuffer addInt:NULL ofSize:testLength];
	[testBuffer addInt:(void*)ninthValue ofSize:0];
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil([testBuffer getIntofSize:0], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals((int32_t) [testBuffer getInt8], (int32_t) firstValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals((int32_t) [testBuffer getInt8], (int32_t) secondValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals((int32_t) [testBuffer getInt16], (int32_t) thirdValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals((int32_t) [testBuffer getInt16], (int32_t) fourthValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals([testBuffer getInt32], fifthValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals([testBuffer getInt32], sixthValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals([testBuffer getInt64], seventhValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals([testBuffer getInt64], eighthValue, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	char* testValueX = (char*) [testBuffer getIntofSize:testLength];
	
	char* s1 = (char*) malloc(testLength + 1);
	memset(s1, 0, testLength + 1);
	memcpy(s1, testValueX, testLength);
	
	char* s2 = (char*) malloc(testLength + 1);
	memset(s2, 0, testLength + 1);
	memcpy(s2, ninthValue, testLength);
	
	if(strcmp(s1, s2) != 0){
		STFail(@"\"%s\" should be \"%s\"", s1, s2);
	}
	
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertEquals((int32_t) [testBuffer getInt8], (int32_t) 0, nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertEquals((int32_t) [testBuffer getInt16], (int32_t) 0, nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertEquals([testBuffer getInt32], (int32_t) 0, nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertEquals([testBuffer getInt64], (int64_t) 0, nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testValueX = [testBuffer getIntofSize:testLength];
	{
		int i;
		for(i = 0; i < testLength; ++i){
			if(testValueX[i] != 0){
				STFail(@"%d should be %d on iteration %d", testValueX[i], 0, i);
			}
		}
	}
	
	STAssertFalse([testBuffer readSucceeded], nil);
	
	free(s2);
	free(s1);
	free(ninthValue);
	
	[testBuffer release];

}

- (void)testDecimals
{
	float firstValue = 1.337;
	double secondValue = 3.141592654;
	
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	
	[testBuffer addFloat:firstValue];
	[testBuffer addDouble:secondValue];
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEqualsWithAccuracy([testBuffer getFloat], firstValue, 0.001, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEqualsWithAccuracy([testBuffer getDouble], secondValue, 0.000000001, nil);
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals([testBuffer getFloat], 0.0f, nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	STAssertEquals([testBuffer getDouble], 0.0, nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer release];
}

- (void)testStrings
{
	char* correctString = "answer";
	NSString* correctNSString = @"answer";
	int correctLength = strlen(correctString);
	
	char* testData = "answer\0answer\0answer\ranswer\nanswer\r\nanswer\ranswer\nanswer\r\nansweransweransweranswer";
	int testLength = 82;
	
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	[testBuffer addData:testData ofSize:testLength];
	
	[testBuffer addData:NULL ofSize:testLength];
	[testBuffer addData:testData ofSize:0];
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil((id) [testBuffer getStringOfSize:0], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil([testBuffer getNSStringOfSize:0], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil((id) [testBuffer getStringTerminatedBy:NULL], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil((id) [testBuffer getStringTerminatedBy:""], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil([testBuffer getNSStringTerminatedBy:NULL], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	STAssertNil([testBuffer getNSStringTerminatedBy:""], nil);
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	char* testString = [testBuffer getNullTerminatedString];
	if(strcmp(testString, correctString) != 0){
		STFail(@"\"%s\" should be \"%s\"", testString, correctString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	NSString* testNSString = [testBuffer getNullTerminatedNSString];
	if(![testNSString isEqualToString:correctNSString]){
		STFail(@"\"%@\" should be \"%@\"", testNSString, correctNSString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testString = [testBuffer getStringTerminatedByLinebreak];
	if(strcmp(testString, correctString) != 0){
		STFail(@"\"%s\" should be \"%s\"", testString, correctString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testString = [testBuffer getStringTerminatedByLinebreak];
	if(strcmp(testString, correctString) != 0){
		STFail(@"\"%s\" should be \"%s\"", testString, correctString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testString = [testBuffer getStringTerminatedByLinebreak];
	if(strcmp(testString, correctString) != 0){
		STFail(@"\"%s\" should be \"%s\"", testString, correctString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testNSString = [testBuffer getNSStringTerminatedByLinebreak];
	if(![testNSString isEqualToString:correctNSString]){
		STFail(@"\"%@\" should be \"%@\"", testNSString, correctNSString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testNSString = [testBuffer getNSStringTerminatedByLinebreak];
	if(![testNSString isEqualToString:correctNSString]){
		STFail(@"\"%@\" should be \"%@\"", testNSString, correctNSString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testNSString = [testBuffer getNSStringTerminatedByLinebreak];
	if(![testNSString isEqualToString:correctNSString]){
		STFail(@"\"%@\" should be \"%@\"", testNSString, correctNSString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testString = [testBuffer getStringTerminatedBy:"r"];
	if(strcmp(testString, correctString) != 0){
		STFail(@"\"%s\" should be \"%s\"", testString, correctString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testNSString = [testBuffer getNSStringTerminatedBy:"r"];
	if(![testNSString isEqualToString:correctNSString]){
		STFail(@"\"%@\" should be \"%@\"", testNSString, correctNSString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testString = [testBuffer getStringOfSize:correctLength];
	if(strcmp(testString, correctString) != 0){
		STFail(@"\"%s\" should be \"%s\"", testString, correctString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:NO];
	testNSString = [testBuffer getNSStringOfSize:correctLength];
	if(![testNSString isEqualToString:correctNSString]){
		STFail(@"\"%@\" should be \"%@\"", testNSString, correctNSString);
	}
	STAssertTrue([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testString = [testBuffer getNullTerminatedString];
	if(testString != NULL){
		STFail(@"\"%s\" should be NULL", testString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testNSString = [testBuffer getNullTerminatedNSString];
	if(testNSString != nil){
		STFail(@"\"%@\" should be nil", testNSString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testString = [testBuffer getStringTerminatedByLinebreak];
	if(testString != NULL){
		STFail(@"\"%s\" should be NULL", testString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testNSString = [testBuffer getNSStringTerminatedByLinebreak];
	if(testNSString != nil){
		STFail(@"\"%@\" should be nil", testNSString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testString = [testBuffer getStringTerminatedBy:"r"];
	if(testString != NULL){
		STFail(@"\"%s\" should be NULL", testString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testNSString = [testBuffer getNSStringTerminatedBy:"r"];
	if(testNSString != nil){
		STFail(@"\"%@\" should be nil", testNSString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testString = [testBuffer getStringOfSize:correctLength];
	if(testString != NULL){
		STFail(@"\"%s\" should be NULL", testString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer protectedSetReadSucceeded:YES];
	testNSString = [testBuffer getNSStringOfSize:correctLength];
	if(testNSString != nil){
		STFail(@"\"%@\" should be nil", testNSString);
	}
	STAssertFalse([testBuffer readSucceeded], nil);
	
	[testBuffer release];
}

- (void)testEquality
{
	BTLSocketBuffer* testBuffer1 = [BTLSocketBuffer new];
	BTLSocketBuffer* testBuffer2 = [BTLSocketBuffer new];
	
	[testBuffer1 addData:"1234567890" ofSize:10];
	[testBuffer2 addData:"1234567890" ofSize:10];
	
	STAssertTrue([testBuffer1 isEqual:testBuffer2], nil);
	
	[testBuffer2 addData:"1" ofSize:1];
	
	STAssertFalse([testBuffer1 isEqual:testBuffer2], nil);
}

- (void)testCopying
{
	BTLSocketBuffer* testBuffer1 = [BTLSocketBuffer new];
	BTLSocketBuffer* testBuffer2 = [[testBuffer1 copy] autorelease];
	STAssertTrue([testBuffer1 isEqual:testBuffer2], nil);
}

- (void)testConcatination
{
	BTLSocketBuffer* testBuffer1 = [BTLSocketBuffer new];
	BTLSocketBuffer* testBuffer2 = [BTLSocketBuffer new];
	
	[testBuffer1 addData:"1234567890" ofSize:10];
	[testBuffer2 addBuffer:testBuffer1];
	STAssertTrue([testBuffer1 isEqual:testBuffer2], nil);
	
	[testBuffer2 addBuffer:testBuffer1];
	STAssertFalse([testBuffer1 isEqual:testBuffer2], nil);
}	

- (void)testSize
{
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	[testBuffer addData:"1234567890" ofSize:10];
	
	STAssertEquals([testBuffer size], (int64_t) 10, nil);
	STAssertEquals([testBuffer remainingSize], (int64_t) 10, nil);
	
	char* throwAway = [testBuffer getStringOfSize:5];
	STAssertEquals([testBuffer remainingSize], (int64_t) 5, nil);
	
	throwAway = [testBuffer getStringOfSize:5];
	STAssertEquals([testBuffer remainingSize], (int64_t) 0, nil);
	
	throwAway = [testBuffer getStringOfSize:5];
	STAssertEquals([testBuffer remainingSize], (int64_t) 0, nil);
	
	[testBuffer release];
}

- (void)testPosition
{
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	[testBuffer addData:"aaaabbbb" ofSize:8];
	STAssertEquals([testBuffer size], (int64_t) 8, nil);
	
	[testBuffer skip:4];
	STAssertEquals([testBuffer remainingSize], (int64_t) 4, nil);
	
	char* testString = [testBuffer getStringOfSize:4];
	if(strcmp(testString, "bbbb") != 0){
		STFail(@"%s should be \"aaaabbbb\"", testString);
	}
	
	[testBuffer unGet:4];
	testString = [testBuffer getStringOfSize:4];
	if(strcmp(testString, "bbbb") != 0){
		STFail(@"%s should be \"bbbb\"", testString);
	}
	
	[testBuffer rewind];
	STAssertEquals([testBuffer remainingSize], (int64_t) 8, nil);
	
	[testBuffer unwind];
	STAssertEquals([testBuffer remainingSize], (int64_t) 0, nil);
	
	[testBuffer rewind];
	STAssertEquals([testBuffer size], (int64_t) 8, nil);
	testString = (char*) malloc([testBuffer size] + 1);
	memset(testString, 0, [testBuffer size] + 1);
	memcpy(testString, [testBuffer rawData], [testBuffer size]);
	if(strcmp(testString, "aaaabbbb") != 0){
		STFail(@"\"%s\" should be aaaabbbb", testString);
	}
	free(testString);
	
	[testBuffer skip:4];
	
	testString = (char*) malloc([testBuffer remainingSize] + 1);
	memset(testString, 0, [testBuffer remainingSize] + 1);
	memcpy(testString, [testBuffer remainingRawData], [testBuffer remainingSize]);
	if(strcmp(testString, "bbbb") != 0){
		STFail(@"\"%s\" should be bbbb", testString);
	}
	free(testString);
	
	STAssertTrue([testBuffer setPosition:0], nil);
	STAssertEquals([testBuffer position], (int64_t) 0, nil);
	
	STAssertTrue([testBuffer setPosition:4], nil);
	STAssertEquals([testBuffer position], (int64_t) 4, nil);
	
	STAssertTrue([testBuffer setPosition:[testBuffer size]], nil);
	STAssertEquals([testBuffer position], [testBuffer size], nil);
	
	STAssertFalse([testBuffer setPosition:-5], nil);
	STAssertEquals([testBuffer position], [testBuffer size], nil);
	
	STAssertTrue([testBuffer setPosition:0], nil);
	STAssertFalse([testBuffer setPosition:[testBuffer size]+5], nil);
	STAssertEquals([testBuffer position], (int64_t) 0, nil);
	
	[testBuffer release];
}

- (void)testEndianFunctions
{	
	char* test = "abcd";
	int length = strlen(test);
	char* result = (char*) flip(test, length);
	{
		int i;
		for(i = 0; i < length; ++i){
			if(test[i] != result[length - 1 - i]){
				STFail(@"%d should be %d at iteration %d", test[i], result[length - 1 - i], i);
			}
		}
	}
	
	free(result);
	
	result = htonx(test, length);
#if BYTE_ORDER == BIG_ENDIAN
	{
		int i;
		for(i = 0; i < length; ++i){
			if(test[i] != result[i]){
				STFail(@"%d should be %d at iteration %d", test[i], result[i], i);
			}
		}
	}
#else
	{
		int i;
		for(i = 0; i < length; ++i){
			if(test[i] != result[length - 1 - i]){
				STFail(@"%d should be %d at iteration %d", test[i], result[length - 1 - i], i);
			}
		}
	}
#endif
	
	free(result);
	
	result = ntohx(test, length);
#if BYTE_ORDER == BIG_ENDIAN
	{
		int i;
		for(i = 0; i < length; ++i){
			if(test[i] != result[i]){
				STFail(@"%d should be %d at iteration %d", test[i], result[i], i);
			}
		}
	}
#else
	{
		int i;
		for(i = 0; i < length; ++i){
			if(test[i] != result[length - 1 - i]){
				STFail(@"%d should be %d at iteration %d", test[i], result[length - 1 - i], i);
			}
		}
	}
#endif
	
	free(result);
}

- (void)testIntEndianess
{
	int16_t firstValue = 0x1234;
	int32_t secondValue = 0x12345678;
	int64_t thirdValue = 0x1234567890ABCDEFLL;
	
	BTLSocketBuffer* testBuffer = [BTLSocketBuffer new];
	
	[testBuffer addInt16:firstValue];
	[testBuffer addInt32:secondValue];
	[testBuffer addInt64:thirdValue];
	
	char* result = [testBuffer getStringOfSize:2];
	
#if BYTE_ORDER == BIG_ENDIAN
	{
		int i;
		for(i = 0; i < 2; ++i){
			if(result[i] != ((char*) &firstValue)[i]){
				STFail(@"0x%X should be 0x%X at iteration %d", result[i], ((char*) &firstValue)[i], i);
			}
		}
	}
#else
	{
		int i;
		for(i = 0; i < 2; ++i){
			if(result[i] != ((char*) &firstValue)[1 - i]){
				STFail(@"0x%X should be 0x%X at iteration %d", result[i], ((char*) &firstValue)[1 - i], i);
			}
		}
	}
#endif
	
	result = [testBuffer getStringOfSize:4];
	
#if BYTE_ORDER == BIG_ENDIAN
	{
		int i;
		for(i = 0; i < 4; ++i){
			if(result[i] != ((char*) &secondValue)[i]){
				STFail(@"0x%X should be 0x%X at iteration %d", result[i], ((char*) &secondValue)[i], i);
			}
		}
	}
#else
	{
		int i;
		for(i = 0; i < 4; ++i){
			if(result[i] != ((char*) &secondValue)[3 - i]){
				STFail(@"0x%X should be 0x%X at iteration %d", result[i], ((char*) &secondValue)[3 - i], i);
			}
		}
	}
#endif
	
	result = [testBuffer getStringOfSize:8];
	
#if BYTE_ORDER == BIG_ENDIAN
	{
		int i;
		for(i = 0; i < 8; ++i){
			if(result[i] != ((char*) &thirdValue)[i]){
				STFail(@"0x%X should be 0x%X at iteration %d", result[i], ((char*) &thirdValue)[i], i);
			}
		}
	}
#else
	{
		int i;
		for(i = 0; i < 8; ++i){
			if(result[i] != ((char*) &thirdValue)[7 - i]){
				STFail(@"0x%X should be 0x%X at iteration %d", result[i], ((char*) &thirdValue)[7 - i], i);
			}
		}
	}
#endif
}

@end
